﻿//----------------------------------------------------------------
// Desc: 
// Edit: ShowLong, http://blog.csdn.net/showlong
//
// Date Created: 2009-10-19
//
// Update Date : 
//
// Copyright (c) Fuel4D (http://www.fuel4d.com), All rights reserved.
//
//----------------------------------------------------------------
#pragma once


#pragma pack(push, 1)


//----------------------------------------------------------------
// 编辑框处理逻辑
//----------------------------------------------------------------
enum EF_CURSOR_MOVE
{
    eCM_Up,
    eCM_Down,
    eCM_Left,
    eCM_Right,
    eCM_Home,
    eCM_End,
    eCM_Position
};
struct FU_EDIT_CURSOR
{
    FUI_DECLARE_STRUCT(FU_EDIT_CURSOR);

    size_t x_idx, l_idx;
    bool shift;
    FU_EDIT_CURSOR()
    {
        shift = false;
        set(0);
    }
    void set(int x, int l = 0)
    {
        x_idx = x, l_idx = l;
    }
};
class FAPI FU_EDIT_CHAR : public IFUI_MemObject
{
    FUI_DECLARE_CLASS(FU_EDIT_CHAR);

public:
    union ECHAR
    {
        WCHAR wc[4];        // UNICODE/UTF16/UTF32
        DWORD dv;           // 用户数据
        DWORD_PTR dp;       // 用户数据
        UINT64 d64;         // 用户数据
        DWORD ud[2];        // 用户数据
    } c;                    // 字符值
    size_t w;               // 字符宽度（不能用符点数）
    size_t h;               // 字符高度（不能用符点数）

    FU_EDIT_CHAR()
    {
        Clear();
    }
    FINLINE void Clear()
    {
        memset(&c, 0, sizeof(c));
        w = h = 0;
    }
};
typedef FUI_Vector< FU_EDIT_CHAR* > FV_UI_PEDIT_CHAR;

class FAPI FU_EDIT_DATA : public IFUI_MemObject
{
    FUI_DECLARE_CLASS(FU_EDIT_DATA);

public:
    FV_UI_PEDIT_CHAR vecChar;// 字符表（组成串）
    FUI_StringW szText;     // 用于访问的
    FUI_StringW szSelect;   // 选中的串
    size_t selStart;        // 选中起点
    size_t selStartPixel;   // 选中位置象素偏移（楧向）
    size_t selEnd;          // 选中结束，也是光标位置(如果两点相等则是光标位置，即没有选块)
    size_t selEndPixel;     // 选中结束的象素偏移（楧向）
    size_t nWidth, nHeight; // 总宽度、总高度（不能用符点数）

    FU_EDIT_DATA(size_t reserve = 32)
    {
        vecChar.reserve(reserve);
        szText.reserve(reserve);
        szSelect.reserve(reserve);

        Clear();
    }
    virtual~FU_EDIT_DATA()
    {
        Clear();
    }
    // 获取字符宽度和高度（必须重载）
    virtual void GetChar(FUI_INIT* uit, const WCHAR* ch, size_t& w, size_t& h) = 0;
    // 分配一个字符（必须重载）
    virtual FU_EDIT_CHAR* AllocChar() = 0;
    // 释放一个字符（必须重载）
    virtual void FreeChar(FU_EDIT_CHAR* p) = 0;
    // 清空
    virtual void Clear()
    {
        for (size_t i = 0; i < vecChar.size(); ++i)
            FreeChar(vecChar[i]);
        vecChar.clear();
        szText.clear();
        szSelect.clear();
        selEnd = selStart = 0;
        nWidth = nHeight = 0;
        selStartPixel = selEndPixel = 0;
    }
    // 是否拥有选择的串
    virtual bool HasSel()
    {
        return selEnd != selStart;
    }
    virtual size_t LeftSel()
    {
        return selEnd < selStart ? selEnd : selStart;
    }
    virtual size_t LeftPixel()
    {
        return selEndPixel < selStartPixel ? selEndPixel : selStartPixel;
    }
    virtual size_t RightSel()
    {
        return selEnd > selStart ? selEnd : selStart;
    }
    virtual size_t RightPixel()
    {
        return selEndPixel > selStartPixel ? selEndPixel : selStartPixel;
    }
    // 取消选择
    virtual void CancelSel()
    {
        selStart = selEnd;
        szSelect.clear();
    }
    // 获取选择数据长度
    virtual size_t SelLength()
    {
        if (selStart > selEnd)
            return selStart - selEnd;
        return selEnd - selStart;
    }
    // 字符数量，不是内存数量
    virtual size_t CharCount()
    {
        return vecChar.size();
    }
    // 获取单行的最小高度（多行编辑来说此值有用）
    virtual size_t MinHeight()
    {
        return 0;
    }
    virtual void RefHeight()
    {
        nHeight = MinHeight();
        for (size_t i = 0; i < vecChar.size(); ++i)
        {
            if ((size_t)vecChar[i]->h > nHeight)
                nHeight = (size_t)vecChar[i]->h;
        }
    }
    virtual void RefPixel()
    {
        if (selEnd == vecChar.size())
            selEndPixel = nWidth;
        else
        {
            selEndPixel = 0;
            for (size_t i = 0; i < selEnd; ++i)
                selEndPixel += (size_t)vecChar[i]->w;
        }

        if (selStart == selEnd)
            selStartPixel = selEndPixel;
        else
        {
            selStartPixel = 0;
            for (size_t i = 0; i < selStart; ++i)
                selStartPixel += (size_t)vecChar[i]->w;
        }
    }
    // 添加字符串
    // .max_str 最大可以输入字符数
    virtual size_t Add(FUI_INIT* uit, const WCHAR* str, size_t max_str, bool add_break, bool is_number)
    {
        if (!str || !*str)
            return 0;

        if (HasSel())
            Del(true);

        const WCHAR* pstr = str;
        FUI_StringW nstr;
        if (is_number)
        {
            FUI_StringA anstr(str);
            int nk = FUI_AtoiA(anstr.c_str());
            anstr = FUI_StringFmtA(_FTA("%d"), nk);
            nstr = anstr;
            pstr = nstr.c_str();
        }
        else
        {
#if defined(ANDROID)
            FUI_StringA anstr(str);
            nstr = anstr;
            pstr = nstr.c_str();
#endif
        }

        size_t ptxt = TextIndex(selEnd);
        size_t pvch = selEnd;
        size_t count = vecChar.size();
        const WCHAR* p = pstr;
        while (*p)
        {
            WCHAR ch = *p;
            if (ch == _FTW('\r') || ch == _FTW('\n'))
            {
                if (!add_break)
                    break;
                FU_EDIT_CHAR* cv = AllocChar();
                cv->c.wc[0] = _FTW('\n');
                szText.insert(ptxt, _FTW('\n'));
                ptxt++;
                p++;
                vecChar.insert(pvch, cv);
                pvch++;
                continue;
            }
            if (vecChar.size() + 1 > max_str)
                break;

            FU_EDIT_CHAR* cv = AllocChar();
            cv->c.wc[0] = ch;
            GetChar(uit, cv->c.wc, cv->w, cv->h);
            szText.insert(ptxt, ch);
            ptxt++;
            p++;
            
            nWidth += (size_t)cv->w;
            vecChar.insert(pvch, cv);
            pvch++;
        }
        Set(selEnd + vecChar.size() - count, false);
        RefHeight();
        return (p - pstr);
    }
    virtual size_t Del(bool backspace)
    {
        if (HasSel())
        {
            size_t ls = LeftSel(), rs = RightSel();
            size_t l_idx = TextIndex(ls);
            szText.erase(l_idx, TextIndex(rs)-l_idx);
            for (size_t i = ls; i < rs; ++i)
                nWidth -= vecChar[i]->w;
            size_t kk = ls;
            while (kk < rs)
                vecChar.erase(ls),kk++; // 表删除后索引也变了，这里不用变化ls
            selEnd = ls;
            CancelSel();
            Set(selEnd, false);
        }
        else
        {
            if (backspace && selEnd <= 0)
                return selEnd;
            if (!backspace && selEnd >= vecChar.size())
                return selEnd;
            size_t dst = backspace ? selEnd-1 : selEnd;
            nWidth -= (size_t)(*(vecChar.begin() + dst))->w;
            szText.erase(dst, 1);
            vecChar.erase(dst, 1);
            Set(dst, false);
        }
        RefPixel();
        RefHeight();
        return selEnd;
    }
    // 设置光标
    virtual bool Set(size_t p, bool shift)
    {
        if (p < 0 || p > CharCount())
            return false;
        selEnd = p;
        if (!shift)
            selStart = selEnd;

        RefPixel();

        szSelect.clear();
        if (HasSel())
        {
            size_t t1 = TextIndex(LeftSel());
            size_t t2 = TextIndex(RightSel());
            szSelect.insert(0, szText.GetString()+t1, t2-t1);
        }

        return true;
    }
    // 移动光标
    virtual void Move(EF_CURSOR_MOVE dir, FU_EDIT_CURSOR& ec)
    {
        switch (dir)
        {
        case eCM_Left:
            Set(selEnd - (selEnd > 0 ? 1 : 0), ec.shift);
            break;
        case eCM_Right:
            Set(selEnd + (selEnd < vecChar.size() ? 1 : 0), ec.shift);
            break;
        case eCM_Home:
            Set(0, ec.shift);
            break;
        case eCM_End:
            Set(vecChar.size(), ec.shift);
            break;
        case eCM_Position:
            Set(ec.x_idx, ec.shift);
            break;
        }
    }
    // 获取渲染偏移
    virtual funit Offset()
    {
        return 0;
    }
    // 获取字符显示编移
    virtual size_t StartOffset()
    {
        return 0;
    }
    // 计算机指定字符在文本串的索引 [及该索引的象素位置]
    virtual size_t TextIndex(size_t p, size_t* pixel = 0)
    {
        size_t ptxt = 0;
        if (pixel)
            *pixel = 0;
        for (size_t i = 0; i < p && i < vecChar.size(); ++i)
        {
            FU_EDIT_CHAR* ec = vecChar[i];
            ptxt++;
            if (pixel)
                *pixel += (size_t)ec->w;
        }
        return ptxt;
    }
    // 通过象素位置获取字符索引（单行）
    // .cx 是相对于控件工作区的编移
    virtual size_t CursorIndex(funit cx, size_t force_w = 0)
    {
        if (cx <= 0)
            return 0;
        for (size_t i = StartOffset(); i < vecChar.size(); ++i)
        {
            size_t w = force_w ? force_w : (size_t)vecChar[i]->w;
            if (cx < w)
            {
                if (cx / (float)w > 0.5f)
                    return i + 1;
                return i;
            }
            cx -= (funit)w;
        }
        return vecChar.size();
    }
    // 从指定字符索引开始向前找到指定象素区间最远的一个字符索引
    virtual size_t FarIndex(size_t idx, size_t pixel)
    {
        if (pixel <= 1)
            return idx;
        if (idx <= 0)
            return 0;
        if (idx > vecChar.size())
            idx = vecChar.size();
        for (size_t i = idx; i > 0; --i)
        {
            if (pixel < (size_t)vecChar[i-1]->w)
                return i;
            pixel -= (size_t)vecChar[i-1]->w;
        }
        return 0;
    }
    // 渲染选择块
    virtual void RenderBlock(FUI_INIT* uit, fuRect& rc, DWORD color, bool auto_clip, bool is_pwd)
    {
        if (CharCount() <= 0 || !HasSel())
            return;

        if (auto_clip)
            uit->cliprect(uit, &rc);

        fuRect selrc = rc;
        selrc.uPos.nX -= Offset();
        if (is_pwd)
        {
            size_t ww = 0, hh = 0;
            GetChar(uit, _FTW("*"), ww, hh);
            selrc.uPos.nX += (funit)LeftSel() * ww;
            selrc.uSize.nW = (funit)(RightSel() - LeftSel()) * ww;
        }
        else
        {
            selrc.uPos.nX += (funit)LeftPixel();
            selrc.uSize.nW = (funit)(RightPixel() - LeftPixel());
        }

        uit->drawrect(uit, &selrc, color, true);

        if (auto_clip)
            uit->cliprect(uit, 0);
    }
    // 渲染文本
    virtual void RenderText(FUI_INIT* uit, fuRect& rc, DWORD fnt_id, fuFontGroup* fg, BYTE efc, FUI_StringW& password, bool ray, DWORD ray_color, bool auto_clip)
    {
        size_t ww = 0, hh = 0;
        if (!password.empty())
            GetChar(uit, _FTW("*"), ww, hh);
        size_t char_count = CharCount();
        funit ofx = Offset();
        funit ofy = (funit)(((int)rc.uSize.nH - (int)(nHeight ? nHeight : fg->nFontSize))>>1);
        if (char_count > 0)
        {
            if (auto_clip)
                uit->cliprect(uit, &rc);

            FUI_StringW* szdst = &szText;
            if (!password.empty())
            {
                password = FUI_StringW(_FTW('*'), CharCount());
                szdst = &password;
            }
            FUI_DrawTextEx(uit, rc.uPos.nX - ofx, rc.uPos.nY + ofy, szdst->c_str(), fnt_id,
                fg->dwFontColor, efc, fg->dwEffectColor);

            if (HasSel())
            {
                if (!password.empty())
                {
                    FUI_StringW pwd_sel(_FTW('*'), SelLength());
                    FUI_DrawTextEx(uit, rc.uPos.nX - ofx + ww * LeftSel(), rc.uPos.nY + ofy, pwd_sel.c_str(), fnt_id, 0xFF000000);
                }
                else
                    FUI_DrawTextEx(uit, rc.uPos.nX - ofx + (funit)LeftPixel(), rc.uPos.nY + ofy, szSelect.c_str(), fnt_id, 0xFF000000);
            }

            if (auto_clip)
                uit->cliprect(uit, 0);
        }

        if (ray)
        {
            funit px = 0;
            if (char_count > 0)
            {
                if (password.empty())
                    px = (funit)selEndPixel;
                else
                    px = (funit)(ww * selEnd);
            }
            fuRect line = rc;
            line.uPos.nX = rc.uPos.nX + px - ofx;
            line.uSize.nW = 2;
            if (HasSel())
                line.uPos.nX += selEnd > selStart ? -1 : 0;
            uit->drawrect(uit, &line, ray_color, true);
        }
    }
};


#pragma pack(pop)
